﻿using Common.Communi.Model;
using Common.Enum;
using ControllerManageServer.Packet;
using Controller.Enum;
using Controller.Model;
using Controller.Utils;
using Leeder.Logger;
using Leeder.Util;
using log4net;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ControllerManageServer.Controller.Prada
{
    /// <summary>
    /// 普达RFID手表控制器
    /// </summary>
    internal class PradaRfidWatchController : IController
    {
        #region 变量声明

        /// <summary>
        /// 连接这个串口的客户端键
        /// </summary>
        public string ConnectionClientKey;
        /// <summary>
        /// 上次通讯时间
        /// </summary>
        private DateTime _lastCommunicationTime;
        /// <summary>
        /// 串口控制器
        /// </summary>
        private PradaSerialCommunicationController _serialController;
        /// <summary>
        /// 日志对象
        /// </summary>
        private LogOperate _log;
        /// <summary>
        /// 串口号
        /// </summary>
        private string _portName;
        /// <summary>
        /// 显示数据
        /// </summary>
        public string DisplayData { get; set; } = "";
        /// <summary>
        /// 未完成指令包
        /// </summary>
        public ConcurrentDictionary<PradaWatchCommunicationCommandEnum, PradaActionPacketModel> UnfinishComandDictionary;
        /// <summary>
        /// 检查间隔时间
        /// </summary>
        public int CheckIntervalTime;
        /// <summary>
        /// 检查数据线程
        /// </summary>
        private Thread CheckDataThread;
        /// <summary>
        /// 是否停止
        /// </summary>
        private bool isStop;
        /// <summary>
        /// 数据显示的X轴位置
        /// </summary>
        private byte positionX;
        /// <summary>
        /// 数据显示的Y轴位置
        /// </summary>
        private byte positionY;
        /// <summary>
        /// 数据显示的字体大小
        /// </summary>
        private byte fontSize;
        /// <summary>
        /// 命令类型和数据报文字典
        /// </summary>
        private ConcurrentDictionary<PradaWatchCommunicationCommandEnum, DatagramInfoModel> DatagramDictionary;

        #endregion 变量声明

        #region 事件定义

        /// <summary>
        /// 数据接收事件,key:应该回给哪个客户端的socket的key(ip:port),返回的数据模型
        /// </summary>
        public event Action<string, ServerPacketModel> OnDataReceive;

        #endregion 事件定义

        public PradaRfidWatchController(SerialPortInfoModel iSerialPortInfo, int iCheckIntervalTime) 
        {
            _log = Log.GetLogger(GetType());
            DatagramDictionary = new ConcurrentDictionary<PradaWatchCommunicationCommandEnum, DatagramInfoModel>();

            _portName = iSerialPortInfo.PortName;
            _serialController = new PradaSerialCommunicationController(iSerialPortInfo, SerialCommunicationModeEnum.PROTOCOL_MODE, 20);
            _serialController.OnSendDataEvent += Controller_SendDataEvent;
            _serialController.OnReceiveDataEvent += Controller_ReceiveDataEvent;
            _serialController.OnConnectionStateChanged += Controller_ConnectionStateChanged;
            _serialController.OnSourceDataReceiveEvent += Controller_SourceDataReceiveEvent;

            ConnectionClientKey = _portName;
            CheckIntervalTime = iCheckIntervalTime;
        }

        /// <summary>
        /// 启动
        /// </summary>
        public void Start() {
            UnfinishComandDictionary = new ConcurrentDictionary<PradaWatchCommunicationCommandEnum, PradaActionPacketModel>();
            _serialController.Start();

            isStop = false;
            CheckDataThread = new Thread(CheckDataFunction) { IsBackground = true};
            CheckDataThread.Start();
        }

        /// <summary>
        /// 控制器发送数据事件
        /// </summary>
        /// <param name="iBuffer"></param>
        private void Controller_SendDataEvent(byte[] iBuffer) {
            _log.Debug($"[Prada] Send >> {_portName}：{ByteUtils.ByteArrayToHexString(iBuffer)}\t{ByteUtils.ByteArrayToAsciiString(iBuffer)}\r\n");
        }

        /// <summary>
        /// 控制器接收数据事件(根据协议处理过的)
        /// </summary>
        /// <param name="iBuffer"></param>
        private void Controller_ReceiveDataEvent(byte[] iBuffer) {
            PradaActionPacketModel model = null;
            PradaWatchCommunicationCommandEnum type = (PradaWatchCommunicationCommandEnum)iBuffer[2];
            switch (type) {
                case PradaWatchCommunicationCommandEnum.RFID_DATA:
                    {
                        model = new PradaRfidDataActionPacketModel();
                        if (!CheckPradaActionPacketModel(model, iBuffer)) return;
                        /// 向手环回复确认接收成功
                        _serialController.SetSendData(new PradaWatchAckPacketModel
                        {
                            AckCommandType = PradaWatchCommunicationCommandEnum.RFID_DATA,
                            ExecutionState = true
                        }.Serialize());
                        // 过滤,如果两条指令接收时间相差小于1.5秒，则跳过
                        if (IsRepeatedDatagram(type, iBuffer)) return;
                        // 发送给客户端
                        SendDataToClient(new TriggerLabelPacketModel
                        {
                            Address = ((PradaRfidDataActionPacketModel)model).Rfid.ToString(),
                            State = DeviceStateEnum.NORMAL,
                            ControllerKey = _portName
                        });
                    }
                    break;
                case PradaWatchCommunicationCommandEnum.ACK: {
                        model = new PradaWatchAckPacketModel();
                        if (!CheckPradaActionPacketModel(model, iBuffer)) return;
                        AckCommandHandler(model as PradaWatchAckPacketModel);
                    }
                    break;
                case PradaWatchCommunicationCommandEnum.REQUEST_DISPLAY_DATA: {
                        model = new PradaRequestDisplayDataPacketModel();
                        if (!CheckPradaActionPacketModel(model, iBuffer)) return;
                        model = new PradaDisplayDataActionPacketModel { DisplayData = DisplayData, PositionX = positionX, PositionY = positionY, FontSize = fontSize };
                        _serialController.SetSendData(model.Serialize());
                        if (UnfinishComandDictionary.ContainsKey(model.Command))
                        {
                            UnfinishComandDictionary[model.Command] = model;
                        }
                        else {
                            UnfinishComandDictionary.TryAdd(model.Command, model);
                        }
                    }
                    break;
            }
            if (!DatagramDictionary.ContainsKey(type))
            {
                DatagramDictionary.TryAdd(type, new DatagramInfoModel { Datagram = iBuffer, ReceiveTime = DateTime.Now });
            }
            else
            {
                DatagramDictionary[type] = new DatagramInfoModel { ReceiveTime = DateTime.Now, Datagram = iBuffer };
            }
            if (model == null || !model.IsSuccess) {
                _log.Error($"无效的命令码!数据报文:{ByteUtils.ByteArrayToHexString(iBuffer)}");
                return;
            }
        }

        /// <summary>
        /// 是否重复数据报文
        /// </summary>
        /// <param name="type">数据类型</param>
        /// <param name="iBuffer">数据报文</param>
        /// <returns></returns>
        private bool IsRepeatedDatagram(PradaWatchCommunicationCommandEnum type, byte[] iBuffer) {
            // 和上一个报文相同并且两个报文接收时间小于1.5秒
            bool result = DatagramDictionary.ContainsKey(type) && DatagramDictionary[type].DatagramIsEqual(iBuffer)
                && (DateTime.Now - DatagramDictionary[type].ReceiveTime).TotalMilliseconds < 1500;

            if (type == PradaWatchCommunicationCommandEnum.RFID_DATA && result) {
                _log.Debug("过滤接收数据!");
            }

            return result;

        }

        /// <summary>
        /// 应答指令处理
        /// </summary>
        /// <param name="iModel"></param>
        private void AckCommandHandler(PradaWatchAckPacketModel iModel)
        {
            if (!iModel.ExecutionState) return;
            if (UnfinishComandDictionary == null || UnfinishComandDictionary.IsEmpty) return;
            PradaActionPacketModel packet;
            switch (iModel.AckCommandType)
            {
                case PradaWatchCommunicationCommandEnum.WRITE_PANID:
                    {
                        if (UnfinishComandDictionary.ContainsKey(iModel.AckCommandType)) UnfinishComandDictionary.TryRemove(iModel.AckCommandType, out packet);
                        _log.Debug("写Pan id指令发送成功!");
                    }
                    break;
                case PradaWatchCommunicationCommandEnum.DISPLAY_DATA:
                    {
                        if (UnfinishComandDictionary.ContainsKey(iModel.AckCommandType)) UnfinishComandDictionary.TryRemove(iModel.AckCommandType, out packet);
                        _log.Debug("显示数据指令发送成功!");
                    }
                    break;
                case PradaWatchCommunicationCommandEnum.WATCH_ALARM:
                    {
                        if (UnfinishComandDictionary.ContainsKey(iModel.AckCommandType)) UnfinishComandDictionary.TryRemove(iModel.AckCommandType, out packet);
                        _log.Debug("手环报警指令发送成功!");
                    }
                    break;
                case PradaWatchCommunicationCommandEnum.WRITE_RFID_DATA:
                    {
                        if (UnfinishComandDictionary.ContainsKey(iModel.AckCommandType)) UnfinishComandDictionary.TryRemove(iModel.AckCommandType, out packet);
                        _log.Debug("写RFID指令发送成功!");
                    }
                    break;
                default:
                    {
                        _log.Error($"应答指令中有无效的命令码!数据报文:{ByteUtils.ByteArrayToHexString(iModel.ToSerialize())}");
                    }
                    break;
            }
        }

        /// <summary>
        /// 检测数据包
        /// </summary>
        /// <param name="iModel">数据模型</param>
        /// <param name="iBuffer">字节数组</param>
        private bool CheckPradaActionPacketModel(PradaActionPacketModel iModel, byte[] iBuffer) {
            iModel.Unserialize(iBuffer);
            if (!iModel.IsSuccess) {
                _log.Error($"无效的命令码!数据报文:{ByteUtils.ByteArrayToHexString(iBuffer)}");
            }
            return iModel.IsSuccess;
        }

        /// <summary>
        /// 控制器接收到的原始报文事件
        /// </summary>
        /// <param name="iBuffer"></param>
        private void Controller_SourceDataReceiveEvent(byte[] iBuffer)
        {
            _log.Debug($"[Prada] Receive << {_portName}：{ByteUtils.ByteArrayToHexString(iBuffer)}\t{ByteUtils.ByteArrayToAsciiString(iBuffer)}\r\n");
        }

        /// <summary>
        /// 控制器连接状态修改事件
        /// </summary>
        /// <param name="iState"></param>
        /// <param name="iPortName"></param>
        private void Controller_ConnectionStateChanged(bool iState, string iPortName) {
            _log.Debug($"[Prada] {_portName} {(iState ? "disconnect" : "connect")}");
        }

        /// <summary>
        /// 停止
        /// </summary>
        public void Stop()
        {
            isStop = true;
            _serialController.Stop();
            try {
                CheckDataThread.Abort();
            }
            catch { }           
        }

        /// <summary>
        /// 设置发送数据
        /// </summary>
        /// <param name="key"></param>
        /// <param name="iPacket"></param>
        public void SetSendData(string key, ServerPacketModel iPacket)
        {
            switch (iPacket.CmdType)
            {
                case CommunicationCmdTypeEnum.DISPLAY_DATA:
                    {
                        DisplayDataPacketModel displayPacket = ((DisplayDataPacketModel)iPacket);
                        DisplayData = displayPacket.DisplayData;
                        positionY = displayPacket.PositionY;
                        positionX = displayPacket.PositionX;
                        fontSize= displayPacket.FontSize;
                    }
                    break;
                case CommunicationCmdTypeEnum.VIBRATION:
                    {
                        PradaWatchActionPacketModel packet = new PradaWatchActionPacketModel {Action = (byte)(((VibrationPacketModel)iPacket).IsVibration ? 0x01 : 0x00) };
                        _serialController.SetSendData(packet.Serialize());
                    }
                    break;
                case CommunicationCmdTypeEnum.WRITE_RFID:
                    {
                        PradaWriteRfidActionPacketModel packet = new PradaWriteRfidActionPacketModel { Rfid = ((WriteRfidPacketModel)iPacket).RFID };
                        _serialController.SetSendData(packet.Serialize());
                    }
                    break;
                case CommunicationCmdTypeEnum.SETTING_PANID_CHANNEL:
                    {
                        PradaPanidChannelActionPacketModel packet = new PradaPanidChannelActionPacketModel
                        {
                            Panid = ((SettingPanIdChannelPacketModel)iPacket).PanId,
                            ChannelNumber = ((SettingPanIdChannelPacketModel)iPacket).ChannelNumber,
                            DutyNumber = ((SettingPanIdChannelPacketModel)iPacket).DutyNumber
                        };
                        _serialController.SetSendData(packet.Serialize());
                    }
                    break;
            }
            ConnectionClientKey = key;
        }

        public override string ToString()
        {
            return _portName;
        }

        /// <summary>
        /// 检查发送指令函数
        /// </summary>
        private void CheckDataFunction() {
            while (!isStop)
            {
                try
                {
                    if (UnfinishComandDictionary != null) { 
                        foreach(var packet in UnfinishComandDictionary.Values) {
                            _serialController.SetSendData(packet.Serialize());
                            Thread.Sleep(200);
                        }
                    }
                }
                catch (Exception ex)
                {
                    _log.Error(ExceptionType.DataAccessException, ex, "Check Data Function Exception!");
                }
                finally {
                    Thread.Sleep(CheckIntervalTime);
                }
            }
        }

        /// <summary>
        /// 发送数据到DPS客户端
        /// </summary>
        /// <param name="iModel">服务端的数据包</param>
        private void SendDataToClient(ServerPacketModel iModel)
        {
            // 数据接收
            OnDataReceive?.BeginInvoke(ConnectionClientKey, iModel, null, null);
        }
    }
}
